#ifndef AT_NETWORK_DEVICE_h
#define AT_NETWORK_DEVICE_h

#include <mutex>
#include <string>
#include <functional>
#include "AtNetworkUrc.h"
#include "AtNetworkStream.h"

class AtNetworkDevice : public AtNetworkStream {
  protected:
    AtNetworkUrc **ustack; // URC栈堆
    AtNetworkSize usize;   // URC栈堆大小

    std::mutex *lock; // 互斥信号量

  public:
    GSM_STRING imei; // 模块IMEI
    GSM_STRING ipv4; // 模块IPV4地址
    GSM_STRING ipv6; // 模块IPV6地址

  public:
    /**
     * 构建AT设备实例
     * @param stream 接口
     * @param bs 缓冲区大小(默认256)
     * @param us URC栈堆大小(默认32)
     */
    AtNetworkDevice(Stream *stream, size_t bs = 256, size_t us = 32);

    /**
     * 初始化网络服务
     * @param timeout_second 超时时长(秒)
     */
    virtual int8_t connect(uint32_t timeout_second = 30) = 0;

    /**
     * 移除回调设置
     * @param at_urc URC回调实体类
     */
    bool remove_urc_callback(AtNetworkUrc *at_urc);

    /**
     * 添加回调设置
     * @param at_urc URC回调实体类
     */
    bool add_urc_callback(AtNetworkUrc *at_urc);

    /**
     * 尝试获取互斥锁
     */
    bool device_lock(uint32_t timeout = 1000);

    /**
     * 释放锁
     */
    void device_unlock();

    /**
     * 释放内存资源
     */
    ~AtNetworkDevice();

  private:
    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param expire_time 超时时间
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     * @param k2 条件2-返回AT_RCODE3
     * @param k3 条件3-返回AT_RCODE4
     * @param k4 条件4-返回AT_RCODE5
     */
    int8_t wait0(uint32_t expire_time, AtKey k0, AtKey k1, AtKey k2, AtKey k3, AtKey k4);

    /**
     * 缓冲区-写入一个字节
     */
    int buffer_write_byte(int data = -1);

    /**
     * 缓冲区-判断是否以指定内容结尾
     */
    bool buffer_end(AtKey suffix);

  public:
    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param timeout 超时时长
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     * @param k2 条件2-返回AT_RCODE3
     * @param k3 条件3-返回AT_RCODE4
     * @param k4 条件4-返回AT_RCODE5
     */
    inline int8_t wait(uint32_t timeout, AtKey k0, AtKey k1, AtKey k2, AtKey k3, AtKey k4) {
      return this->wait0(millis() + timeout, k0, k1, k2, k3, k4);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param timeout 超时时长
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     * @param k2 条件2-返回AT_RCODE3
     * @param k3 条件3-返回AT_RCODE4
     */
    inline int8_t wait(uint32_t timeout, AtKey k0, AtKey k1, AtKey k2, AtKey k3) {
      return this->wait(timeout, k0, k1, k2, k3, AT_ERR);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param timeout 超时时长
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     * @param k2 条件2-返回AT_RCODE3
     */
    inline int8_t wait(uint32_t timeout, AtKey k0, AtKey k1, AtKey k2) {
      return this->wait(timeout, k0, k1, k2, NULL);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param timeout 超时时长
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     */
    inline int8_t wait(uint32_t timeout, AtKey k0, AtKey k1) {
      return this->wait(timeout, k0, k1, NULL);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param timeout 超时时长
     * @param k0 条件0-返回AT_RCODE1
     */
    inline int8_t wait(uint32_t timeout, AtKey k0) {
      return this->wait(timeout, k0, NULL);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     * @param k2 条件2-返回AT_RCODE3
     * @param k3 条件3-返回AT_RCODE4
     */
    inline int8_t wait(AtKey k0, AtKey k1, AtKey k2, AtKey k3) {
      return this->wait(1000L, k0, k1, k2, k3, AT_ERR);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     * @param k2 条件2-返回AT_RCODE3
     */
    inline int8_t wait(AtKey k0, AtKey k1, AtKey k2) {
      return this->wait(k0, k1, k2, NULL);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param k0 条件0-返回AT_RCODE1
     * @param k1 条件1-返回AT_RCODE2
     */
    inline int8_t wait(AtKey k0, AtKey k1) {
      return this->wait(k0, k1, NULL);
    };

    /**
     * 等待串口响应数据符合对应的条件(以r结尾)
     * @param k0 条件0-返回AT_RCODE1
     */
    inline int8_t wait(AtKey k0) {
      return this->wait(k0, NULL);
    };
};

#endif